using Game.Networking;
using Game.Networking.Server;
using Game.UI;
using NaughtyAttributes;
using SMGCore;
using UnityEngine;

namespace Game {
	public partial class ShipCore : WorldEntity<ShipCoreState>, IDamageReceiver {
		[SyncVar(16)]
		public int      Health = 100;
		public ShipGun  ShipGun;
		public uint     OwnerComputerId; //if 0, it's a game controlled NPC
		public string   OwnerName;

		public Transform MissileSpawnPoint;

		public GameObject ShipDamageEffect;
		public GameObject ShipExplosionEffect;

		ShipCargo   _cargo;
		ShipPhysics _physics;
		ShipAPI     _api;
		ShipRadar   _radar;
		ShipReactor _reactor;
		TeamType    _team;
		ShipSignalReceiver _signalReceiver;
		float _initMass = 1;

		public override EntityType EntityType => EntityType.Ship;
		public ShipAPI     Api     => _api;
		public ShipCargo   Cargo   => _cargo;
		public ShipRadar   Radar   => _radar;		
		public ShipReactor Reactor => _reactor;
		public ShipSignalReceiver SignalReceiver => _signalReceiver;

		[ShowNativeProperty]
		public TeamType Team {
			get {
				return _team;
			}
			set {
				_team = value;
				GetComponentInChildren<ColoredTrail>()?.SetMaterial(value);
				GetComponentInChildren<TeamSkin>()?.SetTeamColor(value);
			}
		}

		[SyncVar(17)]
		private float Control_Thrust {
			get {
				return _physics.Thrust;
			}
			set {
				_physics.Thrust = value;
			}
		}

		private void Start() {
			Create();
		}

		public override void Create() {
			if ( _cargo == null ) {
				_cargo = new ShipCargo(this);
			}			
			_physics = GetComponent<ShipPhysics>();
			_reactor = new ShipReactor(this);
			_radar = new ShipRadar(this);
			_api = new ShipAPI(this, _physics);
			_signalReceiver = new ShipSignalReceiver(this);

			base.Create();
			_initMass = Rb.mass;
			if ( !IsServer ) {
				_physics.enabled = false;
			}
			if ( IsServer ) {
				_physics.OnJump.Subscribe(this, f => S_CallRPC_PlayJumpSound(f, true));
				_cargo.OnCargoCollected.Subscribe(this, () => S_CallRPC_PlayCollectSound(true));
				var b = ServerGameController.Instance.GetBaseForTeam(Team);
				if ( b != null ) {
					_signalReceiver.LastReceivedSignalId = b.LastSignalId();
				}
			}
		}

		public bool TryShootGun() {
			if (ShipGun == null) {
				return false;
			}
			if ( !IsServer ) {
				return false;
			}
			if ( !ShipGun.CanShoot() ) {
				return false;
			}
			S_CallRPC_ShootGun(true);
			return true;
		}

		public bool TryLaunchMissile(uint targetId) {
			if ( !IsServer ) {
				return false;
			}
			var missileItem = new AmmoItem() { AmmoType = AmmoType.Missile, Count = 1, ItemType = InventoryItemType.Ammo };
			if ( !_cargo.Inventory.ConsumeItem(missileItem, 1) ) {
				return false;
			}
			ServerEntityController.Instance.CreateNewEntity(EntityType.Projectile, MissileSpawnPoint.position, transform.rotation.eulerAngles, e => {
				var m = (Missile)e;
				if ( m == null) {
					return;
				}
				m.Target= targetId;
				m.Team = Team;
				m.OwnerId = ID;
			});
			S_CallRPC_PlayMissileLaunchSound(true);
			return true;
		}

		public override void Tick(float currentTime, float deltaTime) {
			base.Tick(currentTime, deltaTime);
			if ( IsServer && IsEntityAlive ) {
				_reactor.Update(deltaTime);
				if ( Health <= 0 ) {
					Cargo.TryDropAll();
					Cargo.CreateDebris();
					S_CallRPC_Explode(true);
					DestroyEntity();
				}
			}
		}

		public override void NetTick() {
			base.NetTick();
			if ( !IsServer ) {
				return;
			}
			Rb.mass = _initMass + _cargo.Inventory.ItemCount * 0.01f;
		}

		public int CollectAllCargo() {
			return _cargo.TryCollectAll();
		}

		public void DropAllCargo() {
			_cargo.TryDropAll();
		}

		[ClientRPC(16)]
		void ShootGun() {
			ShipGun.Shoot();
		}

		[ClientRPC(17)]
		void Explode() {
			var effect = Instantiate(ShipExplosionEffect);
			if ( !effect ) {
				return;
			}
			effect.transform.position = transform.position;
			var timer = effect.GetComponent<TimedDestroy>();
			if ( timer ) {
				timer.Activate(5f);
			}
			gameObject.SetActive(false);
		}

		public override void ApplyFullState(EntityState state) {
			base.ApplyFullState(state);
			var cast = (ShipCoreState)state;
			if ( cast == null ) {
				return;
			}
			OwnerComputerId = cast.OwnerUnit;
			OwnerName = cast.OwnerName;
			if ( _cargo == null ) {
				_cargo = new ShipCargo(this);
			}
			_cargo.Inventory.Items = cast.Inventory;
			Team = cast.Team;

		}

		public void ApplyDamage(DamageDescription damage) {
			if ( damage.Damage < 1 ) {
				return;
			}
			if ( !CommonStaticData.CanDealDamage(damage.AttackerTeam, Team) ) {
				return;
			}
			if ( IsServer ) {
				Health -= damage.Damage;
				if ( Health < 0 ) {
					if ( damage.AttackerId > 0 ) {
						ServerAgentsController.Instance.AddKillCountToAgentOwner( damage.AttackerId );
					}
					ServerAgentsController.Instance.AddDeathCountToAgentOwner(ID);
				}
				S_CallRPC_SpawnDamageEffect(damage.HitPoint, damage.HitNormal, true);
			}
			_summary = null;
		}

		[ClientRPC(18)]
		void SpawnDamageEffect(Vector3 hitPoint, Vector3 hitNormal) {
			var effect = Instantiate(ShipDamageEffect, transform);
			if ( !effect ) {
				return;
			}
			effect.transform.position = hitPoint;
			effect.transform.rotation = Quaternion.LookRotation(hitNormal);
			var timer = effect.GetComponent<TimedDestroy>();
			if ( timer ) {
				timer.Activate(1.5f);
			}
			effect.SetActive(true);
		}

		[ClientRPC(19)]
		void PlayJumpSound(float jumpForce) {
			SoundManager.Instance.Play3DSound("ship_jump", transform.position, 0.45f * jumpForce, Random.Range(0.9f, 1.1f), false);
		}

		[ClientRPC(20)]
		void PlayCollectSound() {
			SoundManager.Instance.Play3DSound("cargo_collect", transform.position, 0.35f, 1, false);
		}

		[ClientRPC(21)]
		void PlayMissileLaunchSound() {
			SoundManager.Instance.Play3DSound("rocket_launch", transform.position, 0.35f, 1, false);
		}

		protected override ShipCoreState SaveInternal() {
			var s = base.SaveInternal();
			s.OwnerUnit = OwnerComputerId;
			s.Team      = Team;
			s.OwnerName = OwnerName;
			s.Inventory = _cargo.Inventory.Items;
			return s;
		}

		protected override void SDestroy_Internal() {
			base.SDestroy_Internal();
			ServerAgentsController.Instance.OnAgentEntityDestroyed(OwnerComputerId);
		}

		public TeamType GetTeam() {
			return Team;
		}

		protected override string CreateSummary() {
			var owner = string.IsNullOrEmpty(OwnerName) ? "NPC" : "OwnerName";
			return $"type:ship,team:{Team},health:{Health},owner:{owner}";
		}
	}
}
